package service

import (
	"context"
	"errors"
	"gitee.com/arjunxw/iothub-south-client/config"
	"gorm.io/gorm"
)

type DalInfoService interface {
	FindById(ctx context.Context, id int64) (dalInfo *DalInfo)
	FindByDalId(ctx context.Context, dalId string) (dalInfo *DalInfo)
	FindAll(ctx context.Context) (all []*DalInfo)
	Create(ctx context.Context, dalInfo *DalInfo) (err error)
	Update(ctx context.Context, dalInfo *DalInfo) (err error)
}

type dalInfoService struct {
	cfg config.Config
	db  *gorm.DB
}

func NewDalInfoService(cfg config.Config, db *gorm.DB) DalInfoService {
	service := &dalInfoService{cfg: cfg, db: db}
	return service
}

func (dis *dalInfoService) Init() (err error) {
	dalInfo := dis.cfg.GetDalInfo()
	if dalInfo == nil || len(dalInfo) == 0 {
		return errors.New("dalinfo is empty, please check your configuration file.")
	}

	if err = dis.db.AutoMigrate(&DalInfo{}); err != nil {
		return
	}
	if err = dis.db.Transaction(func(tx *gorm.DB) (err error) {
		ctx := context.WithValue(context.Background(), TransactionKey{}, tx)
		for k, v := range dalInfo {
			di := dis.FindByDalId(ctx, k)
			if di == nil {
				di = &DalInfo{DalID: k, Secret: v}
				if err = tx.Create(di).Error; err != nil {
					return
				}
			} else {
				di.DalID = k
				di.Secret = v
				if err = tx.Model(di).Updates(di).Error; err != nil {
					return
				}
			}
		}
		return
	}); err != nil {
		return err
	}
	return nil
}

func (dis *dalInfoService) getDB(ctx context.Context) *gorm.DB {
	val := ctx.Value(TransactionKey{})
	switch val := val.(type) {
	case *gorm.DB:
		return val
	default:
		return dis.db
	}
}

func (dis *dalInfoService) FindById(ctx context.Context, id int64) (dalInfo *DalInfo) {
	dalInfo = new(DalInfo)
	if err := dis.getDB(ctx).First(dalInfo, "id = ?", id).Error; err == nil {
		return dalInfo
	}
	return nil
}

func (dis *dalInfoService) FindByDalId(ctx context.Context, dalId string) (dalInfo *DalInfo) {
	dalInfo = new(DalInfo)
	if err := dis.getDB(ctx).First(dalInfo, "dal_id = ?", dalId).Error; err == nil {
		return dalInfo
	}
	return nil
}

func (dis *dalInfoService) FindAll(ctx context.Context) (all []*DalInfo) {
	all = make([]*DalInfo, 0)
	if err := dis.getDB(ctx).Find(all).Error; err == nil {
		return all
	}
	return nil
}

func (dis *dalInfoService) Create(ctx context.Context, dalInfo *DalInfo) (err error) {
	return dis.getDB(ctx).Create(dalInfo).Error
}

func (dis *dalInfoService) Update(ctx context.Context, dalInfo *DalInfo) (err error) {
	return dis.getDB(ctx).Model(dalInfo).Updates(dalInfo).Error
}
